﻿using System;
using System.IO;
using System.Net;
using System.Reflection;
using System.ServiceModel.Channels;
using System.Xml;
using System.Xml.Serialization;
using Microsoft.Http;

namespace Interscape.TwilioClient.ServiceHelpers
{
    public sealed class ServiceUtility
    {

        private ServiceUtility()
        {
        }

        /// <summary>
        /// Deserializes a TwilioAPI response from a WCF Message.
        /// </summary>
        /// <param name="message"></param>
        /// <param name="returnType"></param>
        /// <returns></returns>
        /// <remarks>This method is used primarily by the TwilioWcfSerializer WCF helper class.</remarks>
        public static object DeserializeFromMessage(Message message, Type returnType)
        {
            XmlDocument xdoc = new XmlDocument();
            XmlReader xr = message.GetReaderAtBodyContents();
            xdoc.Load(xr);
            xr.Close();
            return DeserializeFromXmlDocument(xdoc, returnType);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="xdoc">The XmlDocument that contains teh object to be deserialized.</param>
        /// <param name="returnType"></param>
        /// <returns></returns>
        /// <remarks>This method allows us to deserialize objects received from PUTs or POSTs via WCF REST Toolkit.</remarks>
        public static object DeserializeFromXmlDocument(XmlDocument xdoc, Type returnType)
        {
            MemoryStream ms = new MemoryStream();
            XmlWriter xw = XmlWriter.Create(ms);
            xdoc.WriteContentTo(xw);
            xw.Flush();
            xw.Close();
            ms.Position = 0;
            XmlReader xr = XmlReader.Create(ms);
            object result = DeserializeFromXmlReader(ref xr, returnType);
            xr.Close();

            if (returnType.BaseType.Name.Contains("PagedList"))
            {
                ms.Position = 0;
                XmlReader xr2 = XmlReader.Create(ms);
                FixPagedListFromXmlReader(ref result, ref xr2, returnType);
            }
            ms.Close();
            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="xdr"></param>
        /// <param name="returnType"></param>
        /// <returns></returns>
        public static object DeserializeFromXmlReader(ref XmlReader xdr, Type returnType)
        {
            XmlSerializer serializer = new XmlSerializer(returnType);
            xdr.MoveToContent();

            if (xdr.Name == "TwilioResponse")
            {
                xdr.Read();
                return serializer.Deserialize(xdr.ReadSubtree());
            }
            return serializer.Deserialize(xdr);
        }

        /// <summary>
        /// This method is a hack to deal with the fact that the Serializer only handles items in a Generic List, not properties.
        /// </summary>
        /// <param name="pagedList">A reference to the PagedList object to add Twilio paging data to.</param>
        /// <param name="xdr">A reference to the XmlReader that contains the object we're gonna parse for pagind data.</param>
        /// <param name="returnType"></param>
        /// <remarks>This method will close the referenced XmlReader, so make sure your code does not reference it further.</remarks>
        static void FixPagedListFromXmlReader(ref object pagedList, ref XmlReader xdr, Type returnType)
        {
            int page;
            int pagesize;
            int numpages;
            int total;
            int start;
            int end;

            xdr.MoveToContent();
            xdr.Read();
            xdr.MoveToFirstAttribute();

            page = int.Parse(xdr.GetAttribute("page"));
            numpages = int.Parse(xdr.GetAttribute("numpages"));
            pagesize = int.Parse(xdr.GetAttribute("pagesize"));
            total = int.Parse(xdr.GetAttribute("total"));
            start = int.Parse(xdr.GetAttribute("start"));
            end = int.Parse(xdr.GetAttribute("end"));
            xdr.Close();

            foreach (PropertyInfo propBase in returnType.GetProperties())
            {
                switch (propBase.Name)
                {
                    case "Page":
                        propBase.SetValue(pagedList, page, null);
                        break;
                    case "PageCount":
                        propBase.SetValue(pagedList, numpages, null);
                        break;
                    case "PageSize":
                        propBase.SetValue(pagedList, pagesize, null);
                        break;
                    case "ItemCount":
                        propBase.SetValue(pagedList, total, null);
                        break;
                    case "StartIndex":
                        propBase.SetValue(pagedList, start, null);
                        break;
                    case "EndIndex":
                        propBase.SetValue(pagedList, end, null);
                        break;
                }
            }
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="response"></param>
        /// <param name="returnType"></param>
        /// <returns></returns>
        public static object ProcessHttpResponseMessage(HttpResponseMessage response, Type returnType)
        {
            XmlDocument xdoc = new XmlDocument();
            XmlReader xr = response.Content.ReadAsXmlReader();
            xdoc.Load(xr);
            xr.Close();

            //Check for bad calls and return a RestException
            switch (response.StatusCode)
            {
                case HttpStatusCode.NotFound:
                case HttpStatusCode.Forbidden:
                case HttpStatusCode.BadRequest:
                case HttpStatusCode.MethodNotAllowed:
                case HttpStatusCode.Unauthorized:
                case HttpStatusCode.InternalServerError:
                    TwilioRestError ex = (TwilioRestError)ServiceUtility.DeserializeFromXmlDocument(xdoc, typeof(TwilioRestError));
                    throw new TwilioClientException(ex.Message, ex, xdoc);
            }

            return ServiceUtility.DeserializeFromXmlDocument(xdoc, returnType);

        }


    }
}
